﻿/**
*┌──────────────────────────────────────────────────────────────┐
*│　描    述：PostgreSQL的操作工具类                                                  
*│　作    者：执笔小白                                              
*│　版    本：1.0                                       
*│　创建时间：2023-6-22 17:01:56                            
*└──────────────────────────────────────────────────────────────┘
*┌──────────────────────────────────────────────────────────────┐
*│　命名空间: Util.SQLHelpers                               
*│　类    名：PostgreSQLHelper                                     
*└──────────────────────────────────────────────────────────────┘
*/
using Npgsql;
using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Text;

namespace Util.SQLHelpers
{
    /// <summary>
    /// PostgreSQL的操作工具类
    /// </summary>
    public class PostgreSQLHelper
    {
        #region 变量
        /// <summary>
        /// 连接字符串
        /// PostgreSQLConStr:HOST=localhost;PORT=5432;DATABASE=testdb;USER ID=root;PASSWORD=Aa123456;
        /// </summary>
        private static string str = ConfigurationManager.ConnectionStrings["PostgreSQLConStr"].ConnectionString;
        #endregion 变量

        #region 初始化
        /// <summary>
        /// 初始化数据库链接
        /// </summary>
        /// <param name="conStr">数据库连接字符串</param>
        public static void Initial(string conStr)
        {
            str = conStr;  // 
        }
        #endregion 初始化

        /* 返回的是受影响的行数
         * ExecuteNonQuery()通常情况下为数据库事务处理的首选，当需要执行插入，删除，修改等操作时，首选ExecuteNonQuery(),不适用于Selete语句，返回永远是-1
         * ExecuteNonQuery()执行成功返回的是一受影响的行数，对于"Create Table"和"Drop Table"语句，返回值是0，
         * ExecuteNonQuery()操作数据时，可以不使用DataSet直接更改数据库中的数据。
         */
        public static int ExecuteNonQuery(string sql, params NpgsqlParameter[] ps)
        {
            using (NpgsqlConnection con = new NpgsqlConnection(str))
            {
                using (NpgsqlCommand cmd = new NpgsqlCommand(sql, con))
                {
                    if (ps != null)
                    {
                        cmd.Parameters.AddRange(ps);
                    }
                    con.Open();

                    int cc = cmd.ExecuteNonQuery();
                    con.Close();
                    return cc;
                }
            }
        }

        /* ExecuteScalar()方法也可以用来执行SQL语句，但是executescalar()执行SQL语句后返回值与ExecuteNonQuery()并不相同，
         * ExecuteNonQuery()操作后返回的是一个值，而executescalar()操作后则会返回一个对象（object），
         * 如果执行的SQL语句是查询语句，则返回结果是查询后的第一行第一列，
         * 如果执行的SQL语句不是一个查询语句，则会返回一个未实例化的对象，必须通过类型装换来显示。
         * 
         * executescalar()经常使用当需要返回单一值时的情况。例如当插入一条数据信息时，
         * 需要马上知道刚才插入的值，则可以使用executescalar()方法。
         */
        public static object ExecuteScalar(string sql, params NpgsqlParameter[] ps)
        {
            using (NpgsqlConnection con = new NpgsqlConnection(str))
            {
                using (NpgsqlCommand cmd = new NpgsqlCommand(sql, con))
                {
                    if (ps != null)
                    {
                        cmd.Parameters.AddRange(ps);
                    }
                    con.Open();//打开数据库

                    object cc = cmd.ExecuteScalar();
                    con.Close();
                    return cc;
                }
            }
        }

        /* 快速查询
         * ExecuteReader比DataSet而言，DataReader具有较快的访问能力，并且能够使用较少的服务器资源。
         * DataReader对象提供了游标形式的读取方式，当从结果行中读取了一行，则游标会继续读取到下一行。
         * 通过read方法可以判断数据是否还有下一行，如果存在数据，则继续运行返回true，如果没有数据，则返回false。
         */
        public static DataTable ExecuteReader(string sql, params NpgsqlParameter[] ps)
        {
            using (NpgsqlConnection con = new NpgsqlConnection(str))
            {
                using (NpgsqlCommand cmd = new NpgsqlCommand(sql, con))
                {
                    if (ps != null)
                    {
                        cmd.Parameters.AddRange(ps);
                    }
                    con.Open();
                    NpgsqlDataReader reader = cmd.ExecuteReader();
                    DataTable datatable = new DataTable();

                    // 添加DataTable列
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        DataColumn myDataColumn = new DataColumn();
                        myDataColumn.DataType = reader.GetFieldType(i);
                        myDataColumn.ColumnName = reader.GetName(i);
                        datatable.Columns.Add(myDataColumn);
                    }

                    // 添加DataTable数据
                    while (reader.Read())
                    {
                        //var n1 = reader.GetInt32(0);   // 第一列值
                        //var n2 = reader.GetString(1);  // 第二列值

                        DataRow myDataRow = datatable.NewRow();
                        for (int i = 0; i < reader.FieldCount; i++)
                        {
                            myDataRow[i] = reader[i].ToString();
                        }
                        datatable.Rows.Add(myDataRow);
                    }

                    reader.Close();
                    //con.Open();
                    cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);  // 关闭关联的connection

                    return datatable;
                }
            }
        }

        /* 通用查询方法
         * 为了将数据库的数据填充到dataset中，则必须先使用adapter对象的方法实现填充，当数据填充完成后，
         * 开发人员可以将记录添加到dataset对象中，然后使用update方法将数据插入到数据库中。
         */
        public static DataSet SqlDataAdapter_Fill(string sql, params NpgsqlParameter[] ps)
        {
            using (NpgsqlConnection con = new NpgsqlConnection(str))
            {
                using (NpgsqlDataAdapter da = new NpgsqlDataAdapter(sql, con))
                {
                    if (ps != null)
                    {
                        da.SelectCommand.Parameters.AddRange(ps);
                    }
                    con.Open();

                    DataSet dataSet = new DataSet();
                    da.Fill(dataSet);
                    con.Close();
                    return dataSet;
                }
            }
        }

        /* 使用DataSet数据集更新数据　　　　　　　　
         * 为了将数据库的数据填充到dataset中，则必须先使用adapter对象的方法实现填充      
         */
        public static int SqlDataAdapter_Update(string sql, DataSet ds, string tableName)
        {
            using (NpgsqlConnection con = new NpgsqlConnection(str))
            {
                using (NpgsqlDataAdapter sa = new NpgsqlDataAdapter(sql, con))
                {
                    NpgsqlCommandBuilder builder = new NpgsqlCommandBuilder(sa);

                    return sa.Update(ds, tableName);
                }
            }
        }

        #region 事务
        /// <summary>
        /// 返回的是受影响的行数-事务
        /// </summary>
        /// <param name="sql">多个sql</param>
        /// <param name="ps">指定类型的参数</param>
        /// <returns></returns>
        public static int ExecuteNonQueryTrans(string sql, params NpgsqlParameter[] ps)
        {
            using (NpgsqlConnection con = new NpgsqlConnection(str))
            {
                con.Open();
                using (NpgsqlTransaction trans = con.BeginTransaction())
                {
                    try
                    {
                        using (NpgsqlCommand cmd = new NpgsqlCommand())
                        {
                            cmd.Connection = con;
                            cmd.Transaction = trans;
                            cmd.CommandText = sql;
                            if (ps != null)
                            {
                                cmd.Parameters.AddRange(ps);
                            }
                            int cc = cmd.ExecuteNonQuery();

                            trans.Commit();
                            con.Close();
                            return cc;
                        }
                    }
                    catch (Exception ex)
                    {
                        trans.Rollback();
                        con.Close();
                        con.Dispose();
                        throw ex;
                    }
                }
            }
        }
        #endregion
    }
}
